#include <iostream>
#include <string>
#include <vector>
#include <functional>
#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h>

#include "InetAddr.hpp"
#include "ThreadPool.hpp"
#include "LockGuard.hpp"

using task_t = std::function<void()>;

class Rotue
{
public:
    Rotue()
    {
        pthread_mutex_init(&_mutex, nullptr);
    }

    void check_onlineuser(InetAddr &who)
    {
        LockGuard lockguard(&_mutex);
        for (auto &user : _online_user)
        {
            if (user == who)
            {
                LOG(DEBUG, "%s is exists\n", who.AddrStr().c_str());
                return;
            }
        }
        LOG(DEBUG, "%s is not exists, add it\n", who.AddrStr().c_str());
        _online_user.push_back(who);
    }

    void offline(InetAddr &who)
    {
        LockGuard lockguard(&_mutex);

        auto iter = _online_user.begin();
        for (iter; iter != _online_user.end(); iter++)
        {
            if (*iter == who)
            {
                LOG(DEBUG, "%s is offline\n", who.AddrStr().c_str());
                _online_user.erase(iter);
                break;
            }
        }
    }

    void forward_helper(int sockfd, std::string message, InetAddr &who)
    {
        LockGuard lockguard(&_mutex);

        std::string send_message = "[" + who.AddrStr() + "]# " + message;
        for (auto &user : _online_user)
        {
            struct sockaddr_in addr = user.addr();
            LOG(DEBUG, "Forward message to %s, message is %s\n", user.AddrStr().c_str(), send_message.c_str());

            sendto(sockfd, send_message.c_str(), sizeof(send_message), 0, (struct sockaddr *)&addr, sizeof(addr));
            LOG(DEBUG, "sendto success!\n");
        }
    }

    void forward(int sockfd, const std::string &message, InetAddr &who)
    {
        check_onlineuser(who);

        if (message == "QUIT" || message == "Q")
        {
            offline(who);
        }

        // forward_helper(sockfd,message);
        task_t t = bind(&Rotue::forward_helper, this, sockfd, message, who);
        ThreadPool<task_t>::GetInstance()->Push(t);
    }

    ~Rotue()
    {
        pthread_mutex_destroy(&_mutex);
    }

private:
    std::vector<InetAddr> _online_user;
    pthread_mutex_t _mutex;
};